/**
 * Copyright &copy; 2013-2015 山东易科德软件有限公司 All rights reserved.
 */
package cn.net.ecode.modules.sys.utils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.net.ecode.common.config.Global;
import cn.net.ecode.common.mapper.JsonMapper;
import cn.net.ecode.common.persistence.annotation.MyBatisDao;
import cn.net.ecode.common.service.ServiceException;
import cn.net.ecode.common.utils.CacheUtils;
import cn.net.ecode.common.utils.IpUtils;
import cn.net.ecode.common.utils.SpringContextHolder;
import cn.net.ecode.common.utils.StringUtils;
import cn.net.ecode.common.web.Servlets;
import cn.net.ecode.modules.sys.dao.AreaDao;
import cn.net.ecode.modules.sys.dao.CompanyDao;
import cn.net.ecode.modules.sys.dao.MenuDao;
import cn.net.ecode.modules.sys.dao.OfficeDao;
import cn.net.ecode.modules.sys.dao.RoleDao;
import cn.net.ecode.modules.sys.dao.UserDao;
import cn.net.ecode.modules.sys.entity.Area;
import cn.net.ecode.modules.sys.entity.Company;
import cn.net.ecode.modules.sys.entity.Menu;
import cn.net.ecode.modules.sys.entity.Office;
import cn.net.ecode.modules.sys.entity.Role;
import cn.net.ecode.modules.sys.entity.User;
import cn.net.ecode.modules.sys.security.AuthorizingRealm;
import cn.net.ecode.modules.sys.security.AuthorizingRealm.Principal;

/**
 * 用户工具类
 * @author ThinkGem
 * @version 2015-11-5
 */
public class UserUtils {

	protected static Logger logger = LoggerFactory.getLogger(UserUtils.class);
	
	private static UserDao userDao = SpringContextHolder.getBean(UserDao.class);
	private static RoleDao roleDao = SpringContextHolder.getBean(RoleDao.class);
	private static MenuDao menuDao = SpringContextHolder.getBean(MenuDao.class);
	private static AreaDao areaDao = SpringContextHolder.getBean(AreaDao.class);
	private static OfficeDao officeDao = SpringContextHolder.getBean(OfficeDao.class);
	private static CompanyDao companyDao = SpringContextHolder.getBean(CompanyDao.class);
	private static AuthorizingRealm authorizingRealm = SpringContextHolder.getBean(AuthorizingRealm.class);
	
	// 用户缓存常量
	private static final String USER_CACHE = "userCache";
	private static final String USER_CACHE_CODE_ = "code_";
	private static final String USER_CACHE_LOGIN_CODE_ = "login_";
	private static final String USER_CACHE_USER_TYPE_AND_REF_CODE_ = "type_ref_";

	// 会话缓存常量
	public static final String CACHE_MENU_LIST = "menuList";
	public static final String CACHE_OFFICE_LIST = "officeList";
	public static final String CACHE_COMPANY_LIST = "companyList";

	// 系统缓存常量
	public static final String CACHE_AREA_ALL_LIST = "areaAllList";
	
	// 集团缓存常量
	public static final String CACHE_OFFICE_ALL_LIST = "officeAllList";
	public static final String CACHE_COMPANY_ALL_LIST = "companyAllList";

	/**
	 * 用户类型扩展（1：员工，2：会员，3：往来单位，...，n:xxx）
	 */
	@SuppressWarnings("unchecked")
	public static final Map<String, Map<String, String>> USER_TYPE_MAP = (Map<String, Map<String, String>>)
			JsonMapper.fromJsonString(Global.getConfig("user.userTypeMap"), Map.class);
	
	/**
	 * 根据ID获取用户
	 * @param userCode
	 * @return 取不到返回null
	 */
	public synchronized static User get(String userCode){
		HttpServletRequest request = Servlets.getRequest();
		if (request != null){
			// 从请求中获取用户，一次请求如果重复调用，则只从缓存里获取一次
			User u = (User)request.getAttribute("__user__" + userCode);
			if (u != null){
				return u;
			}
		}
		User user = (User)CacheUtils.get(USER_CACHE, USER_CACHE_CODE_ + userCode);
		if (user ==  null && userCode != null){
			User u = new User(userCode);
			user = userDao.get(u);
			user = returnUser(user);
		}
		if (request != null){
			// 保存用户到请求对象，一次请求如果重复调用，则只从缓存里获取一次
			request.setAttribute("__user__" + userCode, user);
		}
		return user;
	}

	/**
	 * 根据ID获取用户
	 * @param userCode
	 * @return 取不到返回null
	 */
	public synchronized static User getByLoginCode(String loginCode, String corpCode){
		String userCode = (String)CacheUtils.get(USER_CACHE, USER_CACHE_LOGIN_CODE_ + (StringUtils.isNotBlank(corpCode) ? corpCode + "_" : "") + loginCode);
		if (userCode == null){
			User user = new User();
			user.setCorpCode(corpCode);
			user.setLoginCode(loginCode);
			user = userDao.getByLoginCode(user);
			return returnUser(user);
		}
		return get(userCode);
	}
	
	/**
	 * 根据用户类型和引用Code获取用户
	 * @param userType
	 * @param refCode
	 * @return
	 */
	public synchronized static User getByTypeAndRef(String userType, String refCode){
		String userCode = (String)CacheUtils.get(USER_CACHE, USER_CACHE_USER_TYPE_AND_REF_CODE_ + userType + "_" + refCode);
		if (userCode == null){
			User user = new User();
			user.setUserType(userType);
			user.setRefCode(refCode);
			user = userDao.getByUserTypeAndRefCode(user);
			return returnUser(user);
		}
		return get(userCode);
	}
	
	/**
	 * UserUtils.get()方法的后续通用执行代码
	 */
	private synchronized static User returnUser(User user){
		if (user == null){
			return null;
		}
		loadRefObj(user); // 加载引用类型对象。
		user.setRoleList(roleDao.findList(new Role(user)));
		CacheUtils.put(USER_CACHE, USER_CACHE_CODE_ + user.getId(), user);
		CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_CODE_ +  (StringUtils.isNotBlank(user.getCorpCode()) ? user.getCorpCode() + "_" : "") + user.getLoginCode(), user.getUserCode());
		CacheUtils.put(USER_CACHE, USER_CACHE_USER_TYPE_AND_REF_CODE_ +  user.getUserType() + "_" + user.getRefCode(), user.getUserCode());
		return user;
	}
	
	/**
	 * 获取当前用户
	 * @return 取不到返回 new User()
	 */
	public static User getUser(){
		Principal principal = getPrincipal();
		if (principal != null){
			User user = get(principal.getId());
			if (user != null) {
				return user;
			}
		}
		// 如果没有登录，则返回实例化空的User对象。
		return new User();
	}
	
	/**
	 * 获取用户类型值（app.properties中的user.userTypeMap配置）
	 * @param userType 用户类型
	 * @param key 配置参数key
	 * @return
	 */
	public static String getUserTypeValue(String userType, String key){
		if (USER_TYPE_MAP == null){
			throw new ServiceException("app.properties中的user.userTypeMap配置不正确或没有定义。");
		}
		Map<String, String> map = USER_TYPE_MAP.get(userType);
		if (map != null && map.get(key) != null){
			return map.get(key);
		}
		logger.warn("app.properties中的user.userTypeMap没有配置“"+userType+"”用户类型的“"+key+"”值。");
		return null;
	}
	
	/**
	 * 根据用户类型加载用户相关详细信息，如员工信息，往来单位
	 * @param user 用户对象
	 * @return
	 */
	public static void loadRefObj(User user){
		if (user.getRefObj() == null && StringUtils.isNotBlank(user.getUserType()) 
				&& StringUtils.isNotBlank(user.getRefCode())){
			String userDaoString = getUserTypeValue(user.getUserType(), "dao");
			if (StringUtils.isNotBlank(userDaoString)){
				try {
					Object dao = SpringContextHolder.getBean(userDaoString);
					// 获取对象类型，并新建一个空的对象
					for (Class<?> cls : dao.getClass().getInterfaces()){
						MyBatisDao ann = cls.getAnnotation(MyBatisDao.class);
						if (ann != null){
							user.setRefObj(ann.entity().getConstructor(String.class).newInstance(user.getRefCode()));
							break;
						}
					}
					// 获取对象值，如果取到值，则设置。
					if (user.getRefObj() != null){
						Object obj = dao.getClass().getMethod("get", Object.class).invoke(dao, user.getRefObj());
						if (obj != null){
							user.setRefObj(obj);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 获取当前用户角色列表(从用户缓存中获取)
	 * @return
	 */
	public static List<Role> getRoleList(){
		return getUser().getRoleList();
	}

	/**
	 * 获取当前用户授权菜单(不缓存，实时抓取)
	 * @return
	 */
	public static List<Menu> getMenuList(){
		return getMenuListByParentCode(null);
	}
	
	/**
	 * 获取当前用户授权菜单
	 * @param parentCode 父级菜单编码
	 * @return
	 */
	public static List<Menu> getMenuListByParentCode(String parentCode){
		@SuppressWarnings("unchecked")
		Map<String, List<Menu>> menuMap = (Map<String, List<Menu>>)getCache(CACHE_MENU_LIST);
		if (menuMap == null){
			menuMap = new ConcurrentHashMap<String, List<Menu>>();
			putCache(CACHE_MENU_LIST, menuMap);
		}
		List<Menu> menuList = null;
		if (parentCode != null){
			menuList = menuMap.get(parentCode);
		}
		if (menuList == null){
			Menu m = new Menu();
			m.setParentCode(parentCode);
			User user = UserUtils.getUser();
			// 获取超级管理员角色菜单
			if (User.isSuperAdmin(user.getUserCode())){
				// 如果是本地访问，则展示全部菜单
				if (IpUtils.isLocalAddr(IpUtils.getRemoteAddr(Servlets.getRequest()))){
					menuList = menuDao.findList(m);
				}else{
					m.setRoleCode(Role.SUPER_ADMIN_ROLE_CODE);
					menuList = menuDao.findByRoleCode(m);
				}
			}
			// 获取系统管理员角色菜单（与超级管理员的区别：系统管理员是给客户用的（有限定权限的，但不受数据权限控制）；超级管理员是给开发或实施用的包括全部权限。）
			else if (User.isSystemAdmin(user.getUserCode())){
				m.setRoleCode(Role.SYSTEM_ADMIN_ROLE_CODE);
				menuList = menuDao.findByRoleCode(m);
			}
			// 获取二级管理员角色菜单
			else if (User.MGR_TYPE_SEC_ADMIN.equals(user.getMgrType())){
				m.setRoleCode(Role.SEC_ADMIN_ROLE_CODE);
				menuList = menuDao.findByRoleCode(m);
			}
			// 获取集团管理员角色菜单
			else if (User.MGR_TYPE_CORP_ADMIN.equals(user.getMgrType())){
				m.setRoleCode(Role.CORP_ADMIN_ROLE_CODE);
				menuList = menuDao.findByRoleCode(m);
			}
			// 普通用户获取普通用户所授权的菜单
			else{
				m.setUserCode(user.getId());
				menuList = menuDao.findByUserCode(m);
			}
			if (parentCode != null){
				menuMap.put(parentCode, menuList);
			}
		}
		return menuList;
	}
	
	/**
	 * 获取所有区域列表（系统级别缓存）
	 * @return
	 */
	public static List<Area> getAreaAllList(){
		@SuppressWarnings("unchecked")
		List<Area> areaList = (List<Area>)getCache(CACHE_AREA_ALL_LIST);
		if (areaList == null){
			areaList = areaDao.findAllList(new Area());
			putCache(CACHE_AREA_ALL_LIST, areaList);
		}
		return areaList;
	}

	/**
	 * 获取部门对象
	 * @param officeCode
	 * @return
	 */
	public static Office getOffice(String officeCode){
		List<Office> officeList = getOfficeAllList();
		for (Office office : officeList){
			if (office.getOfficeCode().equals(officeCode)){
				return office;
			}
		}
		return null;
	}
	
	/**
	 * 获取当前用户有权限访问的机构
	 * @return
	 */
	public static List<Office> getOfficeList(){
		@SuppressWarnings("unchecked")
		List<Office> officeList = (List<Office>)getCache(CACHE_OFFICE_LIST);
		if (officeList == null){
			User user = getUser();
			if (user.isAdmin()){
				officeList = officeDao.findAllList(new Office());
			}else{
				Office office = new Office();
				// 生成数据权限过滤条件
				DataScopeUtils.genSql(office, "dsf", "office_m", "a");
				officeList = officeDao.findList(office);
			}
			putCache(CACHE_OFFICE_LIST, officeList);
		}
		return officeList;
	}

	/**
	 * 获取所有的机构
	 * @return
	 */
	public static List<Office> getOfficeAllList(){
		@SuppressWarnings("unchecked")
		List<Office> officeList = (List<Office>)getCache(CACHE_OFFICE_ALL_LIST);
		if (officeList == null){
			officeList = officeDao.findAllList(new Office());
			putCache(CACHE_OFFICE_ALL_LIST, officeList);
		}
		return officeList;
	}

	/**
	 * 获取公司对象
	 * @param companyCode
	 * @return
	 */
	public static Company getCompany(String companyCode){
		List<Company> companyList = getCompanyAllList();
		for (Company company : companyList){
			if (company.getCompanyCode().equals(companyCode)){
				return company;
			}
		}
		return null;
	}
	
	/**
	 * 获取当前用户授权的公司
	 * @return
	 */
	public static List<Company> getCompanyList(){
		@SuppressWarnings("unchecked")
		List<Company> companyList = (List<Company>)getCache(CACHE_COMPANY_LIST);
		if (companyList == null){
			User user = getUser();
			if (user.isAdmin()){
				companyList = companyDao.findAllList(new Company());
			}else{
				Company company = new Company();
				// 生成数据权限过滤条件
				DataScopeUtils.genSql(company, "dsf", "company_m", "a");
				companyList = companyDao.findList(company);
			}
			putCache(CACHE_COMPANY_LIST, companyList);
		}
		
		return companyList;
	}
	
	/**
	 * 获取所有的公司
	 * @return
	 */
	public static List<Company> getCompanyAllList(){
		@SuppressWarnings("unchecked")
		List<Company> companyList = (List<Company>)getCache(CACHE_COMPANY_ALL_LIST);
		if (companyList == null){
			companyList = companyDao.findAllList(new Company());
			putCache(CACHE_COMPANY_ALL_LIST, companyList);
		}
		return companyList;
	}
	
	/**
	 * 查询集团用户列表
	 * @return
	 */
	public static List<User> findCorpUserList(){
		List<User> corpList;
		User user = new User();
		corpList = userDao.findCorpUserList(user);
		return corpList;
	}
	
	/**
	 * 获取授权主要对象
	 */
	public static Subject getSubject(){
		return SecurityUtils.getSubject();
	}
	
	/**
	 * 获取当前登录者主体对象
	 */
	public static Principal getPrincipal(){
		try{
			Subject subject = getSubject();
			Principal principal = (Principal)subject.getPrincipal();
			if (principal != null){
				return principal;
			}
//			subject.logout();
		}catch (UnavailableSecurityManagerException e) {
			// 什么也不做
		}catch (InvalidSessionException e){
			// 什么也不做
		}
		return null;
	}
	
	/**
	 * 获取会话对象
	 * @return
	 */
	public static Session getSession(){
		try{
			Subject subject = getSubject();
			Session session = subject.getSession(false);
			if (session == null){
				session = subject.getSession();
			}
			if (session != null){
				return session;
			}
//			subject.logout();
		}catch (InvalidSessionException e){
			// 什么也不做
		}
		return null;
	}
	
	/**
	 * 获取缓存值
	 * @param key
	 * @return
	 */
	public static Object getCache(String key) {
		// 系统级别缓存
		if (StringUtils.inString(key, CACHE_AREA_ALL_LIST)){
			return CacheUtils.get(key);
		}
		// 集团级别缓存
		else if (StringUtils.inString(key, CACHE_OFFICE_ALL_LIST, CACHE_COMPANY_ALL_LIST)){
			return CorpUtils.getCache(key);
		}
		// 会话级别缓存
		else{
			return getSession().getAttribute(key);
		}
	}
	
	/**
	 * 获取缓存值
	 * @param key
	 * @param defaultValue
	 * @return
	 */
	public static Object getCache(String key, Object defaultValue) {
		Object value = getCache(key);
		return value != null ? value : defaultValue;
	}

	/**
	 * 设置缓存值
	 * @param key
	 * @param value
	 */
	public static void putCache(String key, Object value) {
		if (StringUtils.inString(key, CACHE_AREA_ALL_LIST)){
			CacheUtils.put(key, value);
		}else if (StringUtils.inString(key, CACHE_OFFICE_ALL_LIST, CACHE_COMPANY_ALL_LIST)){
			CorpUtils.putCache(key, value);
		}else{
			getSession().setAttribute(key, value);
		}
	}

	/**
	 * 移除缓存值
	 * @param key
	 */
	public static void removeCache(String key) {
		if (StringUtils.inString(key, CACHE_AREA_ALL_LIST)){
			CacheUtils.remove(key);
		}else if (StringUtils.inString(key, CACHE_OFFICE_ALL_LIST, CACHE_COMPANY_ALL_LIST)){
			CorpUtils.removeCache(key);
		}else{
			getSession().removeAttribute(key);
		}
		// 清理FoxBPM的工作流缓存
		if (StringUtils.inString(key, CACHE_OFFICE_ALL_LIST)){
			UserUtils.clearFoxbpmCache();
		}
	}

	/**
	 * 清除当前用户缓存
	 */
	public static void clearCache(){
		removeCache(CACHE_MENU_LIST);
		removeCache(CACHE_OFFICE_LIST);
		removeCache(CACHE_COMPANY_LIST);
		UserUtils.clearCache(getUser());
	}
	
	/**
	 * 清除指定用户缓存，不包括改用的SESSION缓存
	 * @param user
	 */
	public static void clearCache(User user){
		// 清理用户缓存
		CacheUtils.remove(USER_CACHE, USER_CACHE_CODE_ + user.getUserCode());
		CacheUtils.remove(USER_CACHE, USER_CACHE_LOGIN_CODE_ + (StringUtils.isNotBlank(user.getCorpCode()) ? user.getCorpCode() + "_" : "") + user.getLoginCode());
		CacheUtils.remove(USER_CACHE, USER_CACHE_USER_TYPE_AND_REF_CODE_ + user.getUserType() + "_" + user.getRefCode());
		// 清理用户权限缓存
		authorizingRealm.clearAllCachedAuthorizationInfo();
		// 清理工作流缓存
		UserUtils.clearFoxbpmCache();
	}

	/**
	 * 仅清理FoxBPM的工作流用户权限方面的缓存。
	 * @param user
	 */
	public static void clearFoxbpmCache(){
		try {
			Class<?> c = Class.forName("org.foxbpm.engine.impl.cache.CacheUtil");
			try {
				c.getMethod("clearIdentityCache").invoke(null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			// 没有这个类，则 忽略
		}
	}
	
}
